package com.zjl.redis.第19章_IO多路复用;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SocketChannel;
import java.util.concurrent.*;

/**
 * 当用户进程调用了 recvfrom (linux的方法) 这个系统调用，kernel（内核） 就开始 了IO的第一个阶段:
 * 准备数据(对于网络IO来说，很多时候数据在一开始还没有到达。
 * 比如，还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。
 * 这个过程需要等待，也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。
 * 而在用户进程这边，整个进程会被阻塞(当然，是进程自己选择的阻塞)。当 kernel一直等到数据准备好了，
 * 它就会将数据从kernel中拷贝到用户内存，然后kernel返回结果，用户进程才解除 block（等待） 的状态，
 * 重新运行起来。所以, BIO的特点就是在IO执行的两个阶段都被block了。
 * 两个阶段的block  ——  服务端接受数据的时间 和 服务端处理数据并返回的时间
 */
public class C_BIO {
    //启动telnet客户端  控制面板-》程序-》启动或关闭Windows功能-》telnet客户端 勾选
    //cmd
    //telnet 127.0.0.1 端口号
    //ctrl+]
    //send 发送的信息
    @Test
    public void 服务端() throws IOException, InterruptedException {
        ExecutorService executorService = new ThreadPoolExecutor(
                2,
                5,
                2L,//线程存活时间
                TimeUnit.SECONDS,//时间单位
                new LinkedBlockingDeque<>(3), //阻塞队列
                Executors.defaultThreadFactory(),//表示生成线程池中工作线程的线程工厂  用于创建线程  一般默认
                new ThreadPoolExecutor.DiscardOldestPolicy()//拒绝策略  当阻塞队列满了  工作线程大于等于最大线程数时触发
        );
        ServerSocket serverSocket = new ServerSocket(5555);
        while (true) {
            Socket socket = serverSocket.accept();//等待连接//一个接受
            System.out.println("连接了一个客户");
            executorService.execute(() -> Input(socket));//多个线程处理请求
            TimeUnit.SECONDS.sleep(1L);
        }
    }

    public static void Input(Socket socket) {
        byte[] bytes = new byte[1024];

        try (
                InputStream inputStream = socket.getInputStream();
//                ByteArrayOutputStream bf = new ByteArrayOutputStream();//缓冲区，可以解决分包边界乱码问题
                OutputStream outputStream = socket.getOutputStream();//自动关闭流
        ){//获取输入流


            /**
             * 方法一：
             * bf.write(inputStream);
             *
             * 方法二：
             * int read;
             * while ((read = inputStream.read(bytes)) != -1) {//客户端必须及时关闭流，否则会一直等着客户端的请求
             *     bf.write(bytes,0,read);
             * }
             * 方法三：
             * byte[] bytes1 = inputStream.readAllBytes();
             *
             * 方法四：
             * int read = inputStream.read(bytes);
             * System.out.println(new String(bytes,0,read));
             *
             * 方法 一 二 三
             *      需要将所有数据读完,才会停止往后走//即客户端必须断掉链接
             * 方法 四
             *      只要读到数据 就会返回，导致每次读取有大小限制（我认为这也是微信有每次消息限制的原因）
             */

            int read = inputStream.read(bytes);
            System.out.println(new String(bytes,0,read));

            outputStream.write("我收到了".getBytes());


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getId() + "*-----结束此链接");
    }

    @Test
    public void 客户端() throws IOException {
        Socket socket = new Socket("127.0.0.1", 5555);

        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧啧".getBytes());
        /**
         * 如果，服务端执行了  方法 一 二 三，
         * outputStream.close();
         * 如果你关闭了输出流，会同时关闭输入流，会导致无法使用输入流；
         * 如果不关闭输出流，服务端就认为接收没停止，就不会给你返回数据
         * 所以每次连接只能  发出或接受
         *
         */


        System.out.println("----------------------" +  socket.isClosed());
        InputStream inputStream = socket.getInputStream();

        System.out.println(new String(inputStream.readAllBytes()));

        outputStream.close();
        inputStream.close();

        socket.close();
    }
}
